package cn.zzf.core;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * 教师课表页面爬取
 *
 * @author GaoFeng2017
 * @date 2018-02-12 17:07:05
 **/

public class ContentCrawer {
    /**
     * url 要爬取的url
     */
    private String url;
    /**
     * referer 防盗链头
     */
    private String referer;

    /**
     * encode 爬取内容所采用的编码
     */
    private String encode;
    /**
     * paramMap 要携带的参数map
     */
    private Map<String,String> paramMap;
    /**
     * filterRegex 过滤爬取内容的正则
     */
    private String filterRegex;
    /**
     * cookie 要携带的cookie
     */
    private BasicClientCookie cookie;

    /**
     * useEntityUtil 是否使用EntityUtils获取爬取的内容
     */
    private boolean useEntityUtils;

    private HttpPost httpPost;
    private CloseableHttpClient client;


    private ContentCrawer(String url, String referer, String encode, Map<String, String> paramMap,
                          String filterRegex, BasicClientCookie cookie ,boolean useEntityUtils) {
        this.url = url;
        this.referer = referer;
        this.encode = encode;
        this.paramMap = paramMap;
        this.filterRegex = filterRegex;
        this.cookie = cookie;
        this.useEntityUtils = useEntityUtils;
        init();
    }

    private void init() {
        try {
            this.httpPost = createHttpPost();
        } catch (URISyntaxException e) {
            throw new ExceptionInInitializerError(e);
        }
        this.client = createHttpClient();
    }
    /**
     * @description:
     * @author: GaoFeng2017
     * @date: 2018/2/12 20:13
     * @param: []
     * @return: org.apache.http.client.entity.UrlEncodedFormEntity
     *
     */
    private HttpPost createHttpPost() throws URISyntaxException {
        if(this.httpPost != null) {
            return httpPost;
        }
        HttpPost httpPost = new HttpPost();

        if (url != null) {
            httpPost.setURI(new URI(this.url));
        }

        if (referer != null) {
            httpPost.setHeader("referer",this.referer);
        }
        return httpPost;
    }

    /**
     * @description:
     * @author: GaoFeng2017
     * @date: 2018/2/12 21:28
     * @param: [httpClient, httpPost]
     * @return: java.lang.String
     *
     */
    private String getContent(CloseableHttpClient httpClient, HttpPost httpPost) {

        InputStream in = null;
        CloseableHttpResponse response = null;

        try {
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            in = entity.getContent();
            String content = "";

            if (useEntityUtils) {
                content = EntityUtils.toString(entity,encode);

            } else {
                byte[] bys = new byte[10240];
                int i = 0;
                while ((i = in.read(bys)) != -1) {
                    content = content + new String(bys, 0, i);
                }
            }

            if (filterRegex != null) {
                content = content.replaceAll(filterRegex, "");
            }

            return content;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            /*释放资源*/
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (in != null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * @description:
     * @author: GaoFeng2017
     * @date: 2018/2/12 21:28
     * @param: []
     * @return: org.apache.http.client.HttpClient
     *
     */
    private CloseableHttpClient createHttpClient() {
        if (this.client != null) {
            return client;
        }

        CookieStore cookieStore = new BasicCookieStore();
        CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
        if (cookie != null) {
            cookieStore.addCookie(cookie);
        }

        return httpclient;
    }

    /**
     * @description:
     * @author: GaoFeng2017
     * @date: 2018/2/12 21:28
     * @param: []
     * @return: org.apache.http.client.entity.UrlEncodedFormEntity
     *
     */
    private UrlEncodedFormEntity createFormEntity() throws UnsupportedEncodingException {

        List<NameValuePair> params = new ArrayList<>();
        if (paramMap != null && !paramMap.isEmpty()) {
            Set<Map.Entry<String, String>> entries = paramMap.entrySet();
            for (Map.Entry<String, String> me : entries) {
                params.add(new BasicNameValuePair(me.getKey(), me.getValue()));
            }

            return new UrlEncodedFormEntity(params, this.getEncode());
        }

        return null;
    }

    public boolean execute(ContentHandler handler, String regex, String tid) {

        try {
            httpPost = this.createHttpPost();
            httpPost.setEntity(this.createFormEntity());
            String content = this.getContent(client,httpPost);
            if (content.contains("验证码错误")) {
                throw new CaptchaInputException();
            }

            if (!content.contains(regex)) {
                return false;
            }
            handler.handle(content,tid);
        } catch (CaptchaInputException e) {
            throw new CaptchaInputException();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return true;
    }

    public void close() {
        if (this.client != null) {
            try {
                client.close();
            } catch (IOException e) {
                client = null;
            }
        }
    }
    public static final class ContentCrawerBuilder {
        private String url;
        private String referer;
        private String encode;
        private Map<String,String> paramMap;
        private String filterRegex;
        private BasicClientCookie cookie;
        private boolean useEntityUtils;

        public ContentCrawerBuilder setUseEntityUtils(boolean useEntityUtils) {
            this.useEntityUtils = useEntityUtils;
            return this;
        }

        public ContentCrawerBuilder setUrl(String url) {
            this.url = url;
            return this;
        }

        public ContentCrawerBuilder setReferer(String referer) {
            this.referer = referer;
            return this;
        }

        public ContentCrawerBuilder setEncode(String encode) {
            this.encode = encode;
            return this;
        }

        public ContentCrawerBuilder setParamMap(Map<String, String> paramMap) {
            this.paramMap = paramMap;
            return this;
        }

        public ContentCrawerBuilder setFilterRegex(String filterRegex) {
            this.filterRegex = filterRegex;
            return this;
        }

        public ContentCrawerBuilder setCookie(BasicClientCookie cookie) {
            this.cookie = cookie;
            return this;
        }

        public ContentCrawer build() {
            return new ContentCrawer(url, referer, encode, paramMap, filterRegex, cookie ,useEntityUtils);
        }
    }

    /** getter and setter */

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
        if (this.httpPost != null) {
            try {
                httpPost.setURI(new URI(this.url));
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public String getReferer() {
        return referer;
    }

    public void setReferer(String referer) {
        this.referer = referer;
    }

    public String getEncode() {
        return encode == null ? "utf-8" : encode;
    }

    public void setEncode(String encode) {
        this.encode = encode;
    }

    public Map<String, String> getParamMap() {
        return paramMap;
    }

    public void setParamMap(Map<String, String> paramMap) {
        this.paramMap = paramMap;
    }

    public String getFilterRegex() {
        return filterRegex;
    }

    public void setFilterRegex(String filterRegex) {
        this.filterRegex = filterRegex;
    }

    public BasicClientCookie getCookie() {
        return cookie;
    }

    public void setCookie(BasicClientCookie cookie) {
        this.cookie = cookie;
    }

}
